// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
  if (typeof exports == 'object' && typeof module == 'object')
    // CommonJS
    mod(require('../../lib/codemirror'));
  else if (typeof define == 'function' && define.amd)
    // AMD
    define(['../../lib/codemirror'], mod);
  // Plain browser env
  else mod(CodeMirror);
})(function(CodeMirror) {
  'use strict';

  var rfc2822 = [
    'From',
    'Sender',
    'Reply-To',
    'To',
    'Cc',
    'Bcc',
    'Message-ID',
    'In-Reply-To',
    'References',
    'Resent-From',
    'Resent-Sender',
    'Resent-To',
    'Resent-Cc',
    'Resent-Bcc',
    'Resent-Message-ID',
    'Return-Path',
    'Received',
  ];
  var rfc2822NoEmail = [
    'Date',
    'Subject',
    'Comments',
    'Keywords',
    'Resent-Date',
  ];

  CodeMirror.registerHelper(
    'hintWords',
    'mbox',
    rfc2822.concat(rfc2822NoEmail),
  );

  var whitespace = /^[ \t]/;
  var separator = /^From /; // See RFC 4155
  var rfc2822Header = new RegExp('^(' + rfc2822.join('|') + '): ');
  var rfc2822HeaderNoEmail = new RegExp(
    '^(' + rfc2822NoEmail.join('|') + '): ',
  );
  var header = /^[^:]+:/; // Optional fields defined in RFC 2822
  var email = /^[^ ]+@[^ ]+/;
  var untilEmail = /^.*?(?=[^ ]+?@[^ ]+)/;
  var bracketedEmail = /^<.*?>/;
  var untilBracketedEmail = /^.*?(?=<.*>)/;

  function styleForHeader(header) {
    if (header === 'Subject') return 'header';
    return 'string';
  }

  function readToken(stream, state) {
    if (stream.sol()) {
      // From last line
      state.inSeparator = false;
      if (state.inHeader && stream.match(whitespace)) {
        // Header folding
        return null;
      } else {
        state.inHeader = false;
        state.header = null;
      }

      if (stream.match(separator)) {
        state.inHeaders = true;
        state.inSeparator = true;
        return 'atom';
      }

      var match;
      var emailPermitted = false;
      if (
        (match = stream.match(rfc2822HeaderNoEmail)) ||
        ((emailPermitted = true) && (match = stream.match(rfc2822Header)))
      ) {
        state.inHeaders = true;
        state.inHeader = true;
        state.emailPermitted = emailPermitted;
        state.header = match[1];
        return 'atom';
      }

      // Use vim's heuristics: recognize custom headers only if the line is in a
      // block of legitimate headers.
      if (state.inHeaders && (match = stream.match(header))) {
        state.inHeader = true;
        state.emailPermitted = true;
        state.header = match[1];
        return 'atom';
      }

      state.inHeaders = false;
      stream.skipToEnd();
      return null;
    }

    if (state.inSeparator) {
      if (stream.match(email)) return 'link';
      if (stream.match(untilEmail)) return 'atom';
      stream.skipToEnd();
      return 'atom';
    }

    if (state.inHeader) {
      var style = styleForHeader(state.header);

      if (state.emailPermitted) {
        if (stream.match(bracketedEmail)) return style + ' link';
        if (stream.match(untilBracketedEmail)) return style;
      }
      stream.skipToEnd();
      return style;
    }

    stream.skipToEnd();
    return null;
  }

  CodeMirror.defineMode('mbox', function() {
    return {
      startState: function() {
        return {
          // Is in a mbox separator
          inSeparator: false,
          // Is in a mail header
          inHeader: false,
          // If bracketed email is permitted. Only applicable when inHeader
          emailPermitted: false,
          // Name of current header
          header: null,
          // Is in a region of mail headers
          inHeaders: false,
        };
      },
      token: readToken,
      blankLine: function(state) {
        state.inHeaders = state.inSeparator = state.inHeader = false;
      },
    };
  });

  CodeMirror.defineMIME('application/mbox', 'mbox');
});
